package net.towee.client.com.stuff.presenter;

import java.util.List;

import com.google.common.base.Preconditions;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.logical.shared.HasOpenHandlers;
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.event.logical.shared.OpenHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.inject.Inject;

import net.towee.client.com.stuff.GroupUsersShower;
import net.towee.client.event.core.UserEnteredToGroupEvent;
import net.towee.client.event.core.UserEnteredToGroupHandler;
import net.towee.client.event.core.UserLeaveFromGroupEvent;
import net.towee.client.event.core.UserLeaveFromGroupHandler;
import net.towee.client.managers.AccountHelper;
import net.towee.client.managers.AccountsManager;
import net.towee.client.managers.GroupsManager;
import net.towee.client.managers.RolesManager;
import net.towee.model.account.AccountGroup;
import net.towee.model.account.AccountIdentity;
import net.towee.model.account.AccountPermission;
import net.towee.model.account.Role;
import net.towee.model.group.GroupData;
import net.towee.model.group.GroupIdentity;
import net.towee.model.group.GroupProfile;
import net.towee.model.utils.Pair;

public class GroupUsersShowerPresenter implements GroupUsersShower, ClickHandler,
		OpenHandler<AccountIdentity>, ValueChangeHandler<Pair<AccountIdentity, Role>> {

	public interface Display extends IsWidget {
		void showUser(AccountIdentity idnt, AccountGroup group, boolean me);
		
		HasClickHandlers getMoreButton();
		
		HasValueChangeHandlers< Pair<AccountIdentity, Role> > getRoleChanger();
		
		HasOpenHandlers< AccountIdentity > getKickUserHandlers();
		
		void setNumberOfUsers(int count);
		
		// Permissions
		void canChangeRoles();
		void canKickUser();
		
		// Events
		void onUsersLoading();
		void onUsersLoaded();
		void noUsers();
		void noMore();

		void addRole(Role r);

		void removeUser(String id);
	}
	
	// Injected
	private final Display display;
	private final GroupsManager groupsManager;
	private final AccountHelper account;
	private final RolesManager rolesManager;
	private final HandlerManager eventBus;
	private final AccountsManager accountsManager;
	
	private int offset = 0;
	private int limit = 20;
	private GroupProfile group;
	
	@Inject
	public GroupUsersShowerPresenter(Display display, GroupsManager groupsManager, AccountHelper account,
			RolesManager rolesManager, HandlerManager eventBus, AccountsManager accountsManager){
		this.display = display;
		this.groupsManager = groupsManager;
		this.account = account;
		this.rolesManager = rolesManager;
		this.eventBus = eventBus;
		this.accountsManager = accountsManager;
		
		addHandlers();
	}
	
	private void addHandlers() {
		display.getRoleChanger().addValueChangeHandler(this);
		display.getKickUserHandlers().addOpenHandler(this);
		display.getMoreButton().addClickHandler(this);
		
		eventBus.addHandler(UserEnteredToGroupEvent.TYPE, new UserEnteredToGroupHandler(){
			@Override
			public void onUserEnteredToGroup(AccountGroup acc) {
				if(acc.getGroupIdentity().getId().equals( group.getId() )) {
					display.showUser(account.getAccount().getAccountIdentity(), acc, true);
					
					// Set permissions
					if(acc.getRole().hasPermissions( AccountPermission.CAN_CHANGE_ROLES))
						display.canChangeRoles();
					if(acc.getRole().hasPermissions( AccountPermission.CAN_KICK_USERS))
						display.canKickUser();
				}
			}
		});
		
		eventBus.addHandler(UserLeaveFromGroupEvent.TYPE, new UserLeaveFromGroupHandler(){
			@Override
			public void onUserLeaveGroup(GroupIdentity identity) {
				if(identity.getId().equals( group.getId() )) 
					display.removeUser( account.getAccount().getId() );
			}
		});
	}

	@Override
	public void go(HasWidgets container) {
		Preconditions.checkNotNull(group, "group is null");
		container.clear();
		container.add( display.asWidget() );
	}

	@Override
	public void setGroupProfile(GroupProfile id) {
		this.group = id;

		// Set number of users
		display.setNumberOfUsers( id.getNumberOfUsers() );
		
		// Set roles
		groupsManager.getGroupData(id.getId(), new AsyncCallback<GroupData>() {
			@Override
			public void onFailure(Throwable caught) {
			}
			@Override
			public void onSuccess(GroupData result) {
				for(Role r : result.getRoleContainer().getRoleList())
					display.addRole( r );
			}
		});
		
		// Set permissions
		if(account.hasPermissions(group.getId(),AccountPermission.CAN_CHANGE_ROLES))
			display.canChangeRoles();
		if(account.hasPermissions(group.getId(),AccountPermission.CAN_KICK_USERS))
			display.canKickUser();
		
		// Get users
		getUsers();
	}
	
	private void getUsers() {
		display.onUsersLoading();
		
		groupsManager.getGroupMembers(group.getId(), offset, limit, new AsyncCallback<List<Pair<AccountIdentity, AccountGroup>>>() {
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(List<Pair<AccountIdentity, AccountGroup>> result) {
				display.onUsersLoaded();

				// Show result
				if(!result.isEmpty())
					for(Pair<AccountIdentity, AccountGroup> p : result) 
						display.showUser(p.getFirst(), p.getSecond(), p.getFirst().getId().equals(account.getAccount().getId()));
				else if(offset == 0)
					display.noUsers();
				
				// Plus offset
				offset += limit;
				
				if(offset >= group.getNumberOfUsers())
					display.noMore();
			}
		});
	}

	/**
	 * On change role
	 */
	@Override
	public void onValueChange(ValueChangeEvent<Pair<AccountIdentity, Role>> event) {
		rolesManager.applyRoleToUser(event.getValue().getSecond(), event.getValue().getFirst().getId(), group.getId(), new AsyncCallback<Void>(){
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Void result) {
			}
		});
	}

	/**
	 * On kick user 
	 */
	@Override
	public void onOpen(OpenEvent<AccountIdentity> event) {
		groupsManager.kickUserFromGroup(group.getId(), event.getTarget().getId(), new AsyncCallback<Boolean>(){
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(Boolean result) {
			}
		});		
	}

	/**
	 * On show more users
	 */
	@Override
	public void onClick(ClickEvent event) {
		getUsers();
	}

}
